import java.util.*;
import java.io.*;

public class QStates {

	private static final int DEFAULT_Q_VALUE = 0;
	private static final String FILE_NAME = "Qdata.dat";

	// store using three hashmaps... the third might get big :)

	private static HashMap<Integer, HashMap<Integer, HashMap<Integer,Double>>> map = new HashMap<Integer, HashMap<Integer, HashMap<Integer,Double>>>();

	public QStates() {
		//map = new HashMap<Integer, HashMap<Integer, HashMap<Integer,Integer>>>();
	}

	// idea: we only allocate positions we tried, no point in allocating others
	public static double getQValue(int[][] board) {

		if (map.get(hash(1,board)) == null) {
			return 0;
		}

		if (map.get(hash(1,board)).get(hash(2,board)) == null) {
			return 0;
		}
		
		if (map.get(hash(1,board)).get(hash(2,board)).get(hash(3,board)) == null) {
			return 0;
		}

		return map.get(hash(1,board)).get(hash(2,board)).get(hash(3,board));
	}
	
	public static void setQValue(int[][] board, Double val) {
		if (map.get(hash(1,board)) == null) {
			map.put(hash(1,board),new HashMap<Integer, HashMap<Integer, Double>>());
		}

		if (map.get(hash(1,board)).get(hash(2,board)) == null) {
			map.get(hash(1,board)).put(hash(2,board),new HashMap<Integer, Double>());
		}
		
		if (map.get(hash(1,board)).get(hash(2,board)).get(hash(3,board)) == null) {
			map.get(hash(1,board)).get(hash(2,board)).put(hash(3,board),val); // test value 1
		} else {
			map.get(hash(1,board)).get(hash(2,board)).remove(hash(3,board));
			map.get(hash(1,board)).get(hash(2,board)).put(hash(3,board),val);
		}
	}

	public static void setValueUsingHash(int one, int two, int three,double val) {
		if (map.get(one) == null) {
			map.put(one,new HashMap<Integer, HashMap<Integer, Double>>());
		}

		if (map.get(one).get(two) == null) {
			map.get(one).put(two,new HashMap<Integer, Double>());
		}
		
		if (map.get(one).get(two).get(three) == null) {
			map.get(one).get(two).put(three,val); // test value 1
		} else {
			map.get(one).get(two).remove(three);
			map.get(one).get(two).put(three,val);
		}
	}

	public static double getValueUsingHash(int one, int two, int three) {

		if (map.get(one) == null) {
			return 0;
		}

		if (map.get(one).get(two) == null) {
			return 0;
		}
		
		if (map.get(one).get(two).get(three) == null) {
			return 0;
		}

		return map.get(one).get(two).get(three);
	}

	// write data to file
	public synchronized static void save() {
		try {
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
			oos.writeObject(map);
			oos.flush();
			oos.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("couldn't write to file");
		}
	}

	public static void load() {

	}
	
	// returns hash for specified part
	public static int hash(int part, int[][] board) { // Specifies which part to hash, 1-3
		int lo = 0;
		int hi = 0;
		switch (part) {
			case 1:	lo = 0;	hi = 1; break;
			case 2: lo = 2; hi = 3; break;
			case 3: lo = 4; hi = 5; break;
			default: 
		}
		
		// generate hash
		int hash = 0;
		int loop = 0;

		for (int i = 0; i < board.length; i++) {
			for (int j = lo; j <= hi; j++) {
				hash += board[i][j]*Math.pow(4,loop);		
				loop++;
				//System.out.println("hashing at " + i + "," + j);
			}
		}	

		return hash;	  
	}


}
